/*
debugout.c

License:
You are welcome to use this software, in source or binary format, for any
purpose, so long as you adhere to the following terms:

1) All distributions of source code must retain this notice, unmodified.
   (Everything within this comment block.)
2) Any executable which includes this source code, modified or not, must
   either:
   a) Include a copy of this notice in a plain text file distributed with the
       executable, or
   b) Display a copy of this notice on an 'about' dialog or similar.
3) If this source code, modified or otherwise, is used in an applocation, the
   versions of the source code used in the application must be freely available
   to anyone who is able to obtain the executable. Source files which are not
   copies or derivatives of those used in <APPNAME> do not need to be available,
   but it is encouraged.

This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall the copyright owner or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and on
any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
*/
//#include "main.h"
#include "debugout.h"

//Globals
unsigned int DebugOut_FileLevel = DO_ALL;
unsigned int DebugOut_ConsoleLevel = DO_ALL;
unsigned int DebugOut_MessageLevel = DO_MAJOR;
FILE* DEBUGOUT_LOGFILE = NULL;
bool DEBUGOUT_CLOSED = false; //if true, the log file was closed, don't re-open it. Prevents destructors from reopening.

/* Prints debug output if enabled
 * Inputs:
 * -Level: Minimum debug level.
 * -Everything else: what to print (works same as printf()).
 */
void DebugOut(unsigned int Level, const TCHAR *fmt, ...)
{
	bool ShowMsg = true; //Show message boxes if enabled
	bool ShowError = true; //Show Windows API error if enabled
	static bool FileNotFound = false;

	if(DEBUGOUT_CLOSED) return;

	if(Level & DO_NO_MSGBOX)
	{
		ShowMsg = false;
		Level = Level & ~DO_NO_MSGBOX;
	}
	if(Level & DO_NO_ERROR)
	{
		ShowError = false;
		Level = Level & ~DO_NO_ERROR;
	}

	TCHAR ERRMSG[1024];
	if((DebugOut_FileLevel == DO_NONE) && (DebugOut_ConsoleLevel == DO_NONE) && (DebugOut_MessageLevel == DO_NONE)) return;
	int e = 0;
	if((Level <= DO_ERR_THRESHOLD) && ShowError)
	{
		#ifndef LINUX
			e = GetLastError();
			GET_SYSTEM_ERROR_MSG(e, ERRMSG, sizeof(ERRMSG));
		#endif //not LINUX
	}
	if((DebugOut_FileLevel > 0) && !DEBUGOUT_LOGFILE && !FileNotFound)
	{
		DEBUGOUT_LOGFILE = fopen(DEBUG_FILENAME, "wt"); //Overwrite existing file in text mode
		if(!DEBUGOUT_LOGFILE)
		{
			printf("[debugout] Unable to open \"" DEBUG_FILENAME "\"\n");
			FileNotFound = true;
		}
		atexit(DEBUGOUT_UNLOAD); //Close the file at exit
	}
	va_list list;
	va_start(list, fmt);
		if(DebugOut_ConsoleLevel >= Level)
		{
			#ifdef DO_HIDE_CONSOLE //If the console-hiding feature is enabled, show the console
				Window_Show(hConsole);
			#endif

			#ifdef SetConsoleColour //If the text colour option is enabled
				if(ColourConsole)
				{
					if(Level == DO_CRITICAL) SetConsoleColour(0x1C); //Red
					else if(Level == DO_MAJOR) SetConsoleColour(0x1D); //Purple
					else if(Level == DO_MINOR) SetConsoleColour(0x1A); //Green
				}
			#endif

			_vtprintf(fmt, list);
			if((Level <= DO_ERR_THRESHOLD) && e) _tprintf(_T("Error %d: %s"), e, ERRMSG);

			#ifdef SetConsoleColour
				SetConsoleColour(DEFAULT_CONSOLE_COLOUR);
			#endif
		}
		if(DEBUGOUT_LOGFILE && (DebugOut_FileLevel >= Level))
		{
			_vftprintf(DEBUGOUT_LOGFILE, fmt, list);
			if((Level <= DO_ERR_THRESHOLD) && e) _ftprintf(DEBUGOUT_LOGFILE, _T("Error %d: %s"), e, ERRMSG);
			fflush(DEBUGOUT_LOGFILE); //Force write immediately (in case of crash)
		}
		#ifndef LINUX
			if((DebugOut_MessageLevel >= Level) && ShowMsg)
			{
				TCHAR msg[4096];
				TCHAR* m;
				_vstprintf(msg, fmt, list);
				m = msg + _tcslen(msg); //Print the error code into the message itself
				if((Level <= DO_ERR_THRESHOLD) && e) _stprintf(m, _T("Error %d: %s"), e, ERRMSG);
				#ifdef DEBUGOUT_CANCEL_BUTTON
					if(MessageBox(NULL, msg, _T("Debug Message"), MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) exit(0);
				#else
					MessageBox(NULL, msg, _T("Debug Message"), MB_ICONINFORMATION);
				#endif
			}
	
			if((Level <= DO_ERR_THRESHOLD) && e) SetLastError(0);
		#endif //not LINUX

		if(Level <= DO_NONE) exit(e);
	va_end(list);
}


/* Prints debug message and exits program
 * Inputs:
 * -msg: Debug message (can't contain variables)
 * Returns: Doesn't.
 * Notes:
 * -Program's return code is the return value from GetLastError() on Windows,
 *  errno on Linux.
 * -Doesn't unload anything; use atexit() to unload.
 * -If msg is NULL, the program exits without printing an error
 *  message. Useful if you've already done so.
 */
void Die(const TCHAR* msg)
{
	#ifdef LINUX
		if(msg) DebugOut(DO_CRITICAL, msg);
		exit(errno);
	#else
		int e = GetLastError();
		if(msg) DebugOut(DO_CRITICAL, msg);
		exit(e);
	#endif
}


/* Closes the debug output file
 * Notes:
 * -Don't call this; it runs at exit automatically.
 */
void DEBUGOUT_UNLOAD()
{
	if(DEBUGOUT_LOGFILE) fclose(DEBUGOUT_LOGFILE);
	DEBUGOUT_LOGFILE = NULL;
	DEBUGOUT_CLOSED = true;
}
